package modules.pathloss;

import datatypes.Antenna;

import java.util.*;

import javax.naming.directory.InvalidAttributesException;

import tools.settings.Settings;
import tools.settings.SettingsMissingException;

/**
 * <p>
 * Represents the link budget, and performs the necessary calculations.
 * </p>
 * <p>
 * Formule: P_rx = P_tx + G_tx + G_rx - L_tx - L_rx - PL, dus als PL gewild is
 * krijgen we PL = bs_power + gains - losses - rcv_sensitivity.
 * </p>
 *
 * @author Lynne Vandousselaere
 */
public class LinkBudgetCalculator {

    private String[] snames = {"ms_antenna_gain", "bs_feeder_loss",
        "ms_feeder_loss", "bs_noise_figure", "ms_noise_figure", "ms_tx_power"};
    // Boltzmann's constant (Ws/K)
    private final static double k = 1.38 * Math.pow(10, -23);
    // Absolute temperature (K)
    private final static double T0 = 290;

    /**
     * <p>Calculates the link budget for the given settings.</p>
     *
     * @param techParam
     *      the needed technology, with specific settings.
     * @param a
     *      the needed antenna, with specific settings.
     * @return the link budget
     * @throws javax.naming.directory.InvalidAttributesException
     *      if the TechSettings lack needed information.
     */
    public double getPathLossComponent(Settings settings, Antenna a, boolean dl)
            throws SettingsMissingException {
        if (!settings.contains(snames))
            throw SettingsMissingException.generate("LinkBudgetCalculator", "", snames);

        return getGainsAndLosses(a, settings, dl) -
                getReceiverSensitivity(settings, dl);
    }

    /**
     *
     * @param a
     * @param settings
     * @return
     * @throws javax.naming.directory.InvalidAttributesException
     */
    private double getGainsAndLosses(Antenna a, Settings settings, boolean dl)
            throws SettingsMissingException {
        double link = 0;
        // Antenna settings
        if (dl)
            link += a.getPower() + a.getGain();
        else
            link += Double.parseDouble(settings.getSettingsValue("ms_tx_power"));

        try {
            for (Iterator<String> it = settings.getSettingsByType("gain", dl).iterator(); it.hasNext();)
                link += Double.parseDouble(settings.getSettingsValue(it.next()));

            for (Iterator<String> it = settings.getSettingsByType("loss", dl).iterator(); it.hasNext();)
                link -= Double.parseDouble(settings.getSettingsValue(it.next()));

            for (Iterator<String> it = settings.getSettingsByType("margin", dl).iterator(); it.hasNext();)
                link -= Double.parseDouble(settings.getSettingsValue(it.next()));

        } catch (NumberFormatException ex) {
            throw new SettingsMissingException(
                    "LinkBudget::NumberFormatException while " +
                    "parsing \"gains, losses & margins\", " +
                    "check XML file. \nEX MSG:" + ex.getMessage());
        }
        return link;
    }

    /**
     *
     * @param settings
     * @return
     * @throws javax.naming.directory.InvalidAttributesException
     */
    private double getReceiverSensitivity(Settings settings, boolean dl)
            throws SettingsMissingException {

        // Constant rcv sensititivity or calculated?
        String rcv = settings.getSettingsValue("rcv_sensitivity", dl);
        if (rcv != null)
            return Double.parseDouble(rcv) + Double.parseDouble(settings.getSettingsValue("noise_figure", dl));

        // 1. using "B"
        String[] rcvnames = {"SNR_rx", "noise_figure", "implementation_loss", "B"};
        if (settings.contains(rcvnames))
            try {
                // Effectively used bandwidth B
                double B = Double.parseDouble(settings.getSettingsValue("B"));
                // receiver SNR, in dB
                double SNR = Double.parseDouble(settings.getSettingsValue("SNR_rx"));
                // Noise figure
                double NF = Double.parseDouble(settings.getSettingsValue("noise_figure", dl));
                // implementation loss
                double imp_loss = Double.parseDouble(settings.getSettingsValue("implementation_loss"));

                return 10 * Math.log10(k * T0 * B) + SNR + NF + imp_loss;

            } catch (NumberFormatException ex) {
                throw new SettingsMissingException(
                        "LinkBudget::NumberFormatException while " +
                        "parsing \"receiver sensitivity\", " +
                        "check XML file. \nEX MSG:" + ex.getMessage());
            }

        // 2. using "Thermal noise"
        rcvnames[3] = "thermal_noise";
        if (!settings.contains(rcvnames))
            throw SettingsMissingException.generate("LinkBudgetCalculator", "calculate receiver sensitivity", rcvnames);

        try {
            // Thermal noise
            double thermal = Double.parseDouble(settings.getSettingsValue("thermal_noise"));
            // receiver SNR, in dB
            double SNR = Double.parseDouble(settings.getSettingsValue("SNR_rx"));
            // Noise figure
            double NF = Double.parseDouble(settings.getSettingsValue("noise_figure", dl));
            // implementation loss
            double imp_loss = Double.parseDouble(settings.getSettingsValue("implementation_loss"));

            return thermal + SNR + NF + imp_loss;

        } catch (NumberFormatException ex) {
            throw new SettingsMissingException(
                    "LinkBudget::NumberFormatException while " +
                    "parsing \"receiver sensitivity\", " +
                    "check XML file. \nEX MSG:" + ex.getMessage());
        }

    }

    public String[] getRequiredSettingNames() {
        return snames;
    }

    public double getPathLossComponent(Settings settings, Antenna a, boolean dl, int mod)
            throws SettingsMissingException {
        if (!settings.contains(snames))
            throw SettingsMissingException.generate("LinkBudgetCalculator", "", snames);

        return getGainsAndLosses(a, settings, dl) -
                getReceiverSensitivity(settings, dl, mod);
    }

    private double getReceiverSensitivity(Settings settings, boolean dl, int mod){
        String downl = dl?"dl":"ul";
        String rcv = settings.getSettingsValue("rcv_sens_".concat(downl).concat("_").concat(Integer.toString(mod)));
        return Double.parseDouble(rcv);
    }
}
